/////////////////
// dragdrop.js //
/////////////////////////
// Version: 2002.05.31 //
//////////////////////////////////////
//                                  //
//  DragDrop.js by Anthony Spears   // 
//                                  //
//////////////////////////////////////

DRAG_DROP_DISTANCE = 25;

/////////////////////////////////////////////////////////////////////
//Set debugMode to true when you want the JS to trap type errors.  //
/////////////////////////////////////////////////////////////////////
var debugMode = true;	

//////////////////////////////////////////////////////////////////////
//  Global drag & drop state variables.  These references point to  //
//  drag objects (not layers).  Please do not change these values!  //
//////////////////////////////////////////////////////////////////////
var lastItemDragged = null;  //Points to the last item that was dragged.
var lastItemDroppedCorrect = null;  //Points to the last item that was dropped into a correct target.
var lastItemDroppedIncorrect = null;  //Points to the last item that was dropped into an incorrect target.
var droppedCount = 0;  //Counts how many items have been dropped into targets.
var correctDroppedCount = 0;  //Counts how many items have been dropped into correct targets.
var statusArray = null;

// New by Scott
var previousTarget = null;

// New by Tony (2/6/2002)
//Setting this value to true will enable rectangular drop checks rather than the normal 
//circular drop checks.  Drop areas will be bounded by the dimensions of the drop target
//layer.
var useLayerBounds = false;

////////////////////////////////////////////////////////////////////////////
//  DO NOT EDIT ANY OF THE VARIABLES BELOW AS THEY MAINTAIN THE DRAG AND  //
//  DROP PAGE STATE.																		  //
////////////////////////////////////////////////////////////////////////////
var ID_DRAGITEM = "drag item";
var ID_DRAGLAYER = "drag layer";
var ID_DROPTARGET = "drop target";

var SNAPBACK_NEVER = 0;
var SNAPBACK_CORRECT = 1;
var SNAPBACK_INCORRECT = 2;
var SNAPBACK_MISS = 4;
var SNAPBACK_ALWAYS = SNAPBACK_CORRECT | SNAPBACK_INCORRECT | SNAPBACK_MISS;

var USMC_640X480 = 0;
var USMC_800X600 = 1;
var FIRSTSGT_640X480 = 2;
var FIRSTSGT_800X600 = 3;

var screenExtentX = 0;
var screenExtentY = 0;
var toolbarStyle = 0;
var dragItemArray = new Array();
var dropTargetArray = new Array();

var isNS = (navigator.appName == 'Netscape');

function getStatusArray()
{
	var statusArray = new Array(dragItemArray.length);
	
	for(var i = 0; i < dragItemArray.length; i++)
	{
		if(dragItemArray[i].occupies == null)
			statusArray[i] = null;
		else
		for(var k = 0; k < dragItemArray[i].targets.length; k++)
		{
			if(dragItemArray[i].targets[k] == dragItemArray[i].occupies)
			{
				if(dragItemArray[i].evalMask[k] == true)
					statusArray[i] = true
				else
					statusArray[i] = false;
					
				break;
			}
		}
	}
	return statusArray
}

			
/*setOnCorrectDrop() sets a drag item's onCorrectDrop property to a javascript string.  
ARGUMENTS:  aDragItem--a reference to a drag item or drag set object.
			   onCorrectJS--a string containing the javascript to be called.
RETURNS:		null if in debug mode and a non-dragitem object is passed into the function,
				true in all other cases.
*/
function setOnCorrectDrop(aDragItem, onCorrectJS)
{
	//DEBUG:  aDragItem is a dragObject or dragSet object.
	if(debugMode == true)
	{
		if(aDragItem.myObjectType != ID_DRAGITEM)
		{
			alert("Error in setOnCorrectDrop:  aDragObject (parameter 1) is not a drag object.");
			return null;
		}
	}
	//End debug.
	
	aDragItem.onCorrectDrop = onCorrectJS;
	return true;
}


/*setOnIncorrectDrop() sets a drag item's onIncorrectDrop property to a javascript string.  
ARGUMENTS:  aDragItem--a reference to a drag item or drag set object.
			   onIncorrectJS--a string containing the javascript to be called.
RETURNS:		null if in debug mode and a non-dragitem object is passed into the function,
				true in all other cases.
*/
function setOnIncorrectDrop(aDragItem, onIncorrectJS)
{
	//DEBUG:  aDragItem is a dragObject or dragSet object.
	if(debugMode == true)
	{
		if(aDragItem.myObjectType != ID_DRAGITEM)
		{
			alert("Error in setOnIncorrectDrop:  aDragObject (parameter 1) is not a drag object.");
			return null;
		}
	}
	//End debug.
	
	aDragItem.onIncorrectDrop = onIncorrectJS;
	return true;
}


/*isCorrectTarget() determines if the given drop target is a correct target for
  the drag set object.
ARGUMENTS:  aDragSet--a reference to a drag set or drag item object.
				aDropTarget--a reference to a drop target object.
RETURNS:		if in debug mode, returns null if the first argument is not a drag set, or the
				second argument is not a drop target.  
				
				Returns true if the target is a correct target for the drag set, false if not.*/
function isCorrectTarget(aDragSet, aDropTarget)
{

	//DEBUG:  aDragSet is an drag set object
	if(debugMode == true)
	{
		//ASSERT:  aDragSet is an drag set object
		if(aDragSet.myObjectType != ID_DRAGITEM)
		{
			alert("Error in isCorrectTarget:  aDragSet (parameter 1) is not a drag object.");
			return null;
		}
	
		//ASSERT:  aDropTarget is an drop target object
		if(aDropTarget.myObjectType != ID_DROPTARGET)
		{
			alert("Error in isCorrectTarget:  aDropTarget (parameter 2) is not a drop target object.");
			return null;
		}
	}
	//End debug.
	
	for(var i = 0; i < aDragSet.targets.length; i++)
	{
		if(aDragSet.targets[i] == aDropTarget)
			return (aDragSet.evalMask[i] == true);
	}
	return false;
}


/*setOnIncorrectDrop() sets a drag item's onIncorrectDrop property to a javascript string.
ARGUMENTS:  aDragItem--a reference to a drag item or drag set object.
			   onIncorrectJS--a string containing the javascript to be called.
RETURNS:		null if in debug mode and a non-dragitem object is passed into the function,
				true in all other cases.
*/ 
function setOnMiss(aDragItem, onMissJS)
{
	
	if(debugMode == true)
	{
		//DEBUG:  aDragItem is a drag object.
		if(aDragItem.myObjectType != ID_DRAGITEM)
		{
			alert("Error in setOnMiss:  aDragObject (parameter 1) is not a drag object.");
			return null;
		}
	}
	//End debug.
	
	aDragItem.onMiss = onMissJS;
	return true;
}


/*setToolbarStyle() specifies the toolbar being used for this courseware.  Be sure to specify
  the correct toolbar style, as this controls the screen extents.
ARGUMENTS:	theToolbarStyle--an integer corresponding to one of the toolbar style constants.
RETURNS:		nothing.
*/
function setToolbarStyle(theToolbarStyle)
{
	
	if(theToolbarStyle == USMC_640X480)
	{
			screenExtentX = 630;
			screenExtentY = 385;
			return;
	}
	else
	if(theToolbarStyle == USMC_800X600)
	{		
			screenExtentX = 790;
			screenExtentY = 500;
			return;
	}
	else
	if(theToolbarStyle == FIRSTSGT_640X480)
	{
			screenExtentX = 555;
			screenExtentY = 430;
			return;
	}
	else
	if(theToolbarStyle == FIRSTSGT_800X600)
	{
		screenExtentX = 715;
		screenExtentY = 550;	
		return;
	}
	else
		alert("Unknown toolbar style.");
}

//MakeDropTarget() creates a new drop target using a layer reference.  Use the primitive utility
//functions or automated utility functions to set this object's properties.
function makeDropTarget(aLayerRef)
{
	var isNS = (navigator.appName == 'Netscape');
	var theLayer = null;
	
	//Get the appropriate layer reference.	
	if(navigator.appName == 'Netscape')
	{
		theLayer = eval("document.layers." + aLayerRef);
	}
	else
	{
		theLayer = eval(aLayerRef);
	}	
	
	//DEBUG: Assert presence of layer's id property.
	if(debugMode == true)
	{
		if(theLayer == null || theLayer.id == null)
		{
			alert("Error in makeDropTarget(): argument[0] is not a layer reference.");
			return null;
		}
	}
	//End debug mode
	
	
	//Set DropTarget's properties.
	this.layerRef = theLayer;	//References the actual layer into which objects may be dropped.
	this.occupiers = new Array();	//Contains references to all objects dropped into this target.
	this.enabled = true;		//Makes this a valid drop target
	this.myObjectType = ID_DROPTARGET; //Tags this object as a drop target
	this.multiDropEnabled = true;	   //Controls ability to drop multiple objects on the same target.
	//this.onCorrectDrop = "";	   //Can be set to evaluate JavaScript code if a correct object is dropped on this target.
	//this.onIncorrectDrop = "";	   //Can be set to evaluate JavaScript code if an incorrect object is dropped on this target.
	if(navigator.appName == "Netscape")
	{
		this.targetTop = theLayer.top;
		this.targetLeft = theLayer.left;
	}
	else
	{
		this.targetTop = theLayer.style.pixelTop;
		this.targetLeft = theLayer.style.pixelLeft;
	}
	
	theLayer.objectRef = this;	   //Points the layer to its drop target representation. 
	theLayer.myObjectType = ID_DROPTARGET;  //Tag drop target with an appropriate ID.
	
	dropTargetArray[dropTargetArray.length] = this;
	return this;
}

/*snapToOrigin() forces a draggable object to snap to its original position.  No checks are
  made to ensure that the object is active, and no object state variables are reset.*/
function snapToOrigin(aDragSet)
{
	snapToPoint(aDragSet,aDragSet.originLeft,aDragSet.originTop);
}


/*
  snapToPoint() forces a draggable object to move to the specified x,y coordinate,
  regardless of its current settings.
  ARGUMENTS:
  aDragSet--Reference to a drag set object.
  x--The horizontal distance to move (corresponds to the "left" layer property)
  y--The vertical distance to move (corresponds to the "top" layer property)
  
  RETURNS:
  true if layer was successfully moved.
  null if the first parameter is invalid.
*/
function snapToPoint(aDragSet,x,y)
{
	if(debugMode == true)
	{
	
		//DEBUG:  aDragSet is a drag set.
		if(aDragSet.myObjectType != ID_DRAGITEM)
		{
			alert("Error in snapToPoint:  aDragSet (parameter 0) is not a drag item.");
			return null;
		}
		
		//ASSERT:  x and y are numbers
		if(typeof(x) != "number" || typeof(y) != "number")
		{
			alert("Error in snapToPoint:  x and y (parameters 1 and 2) must both be numbers.");
			return null;
		}
	}
	//End debug.
	
	//Set new layer coordinates
	if(navigator.appName == 'Netscape')
	{
		aDragSet.layerRef.left = x;
		aDragSet.layerRef.top = y;
	}
	else
	{
		aDragSet.layerRef.style.pixelLeft = x;
		aDragSet.layerRef.style.pixelTop = y;
	}
	return true;
}


/*snapToTarget() snaps a drag set to the target layer indexed by targetIndex.  The layer will only be snapped
  if it is enabled and the drop target will accept the drag object.  If the layer is snapped, the appropriate
  state variables in the drag set will be updated.
  
  ARGUMENTS:
  aDragSet--A reference to a drag set object.
  targetIndex--A number specifiying which target to attempt to snap into.
  
  RETURNS:
  true--if layer was successfully snapped.
  false--if layer could not be snapped.
  null--if an assertion failed.
*/
function snapToTarget(aDragSet,targetIndex)
{
	//ASSERT:  aDragSet is a drag set
	if(debugMode == true && aDragSet.myObjectType != ID_DRAGITEM)
	{
		alert("Error in snapToTarget:  aDragSet (Parameter 1) is not a drag set.");
		return null;
	}
	
	//ASSERT: targetIndex is not greater than the size of aDragSet's target index array.
	if(debugMode == true && targetIndex > aDragSet.targets.length)
	{
		alert("Error in snapToTarget():  targetIndex (Parameter 2) is greater than the number of targets in the given drag set.");
		return null;
	}
	
	//Reference the target object
	var target = aDragSet.targets[targetIndex];

	snapToPoint(aDragSet,target.targetLeft,target.targetTop);  //Move the layer onto the target
}

function invalidateTarget(aDragSet, dropTargetName)
{
	for(var i = 0; i < aDragSet.targets.length; i++)
	{
		if(dropTargetName == aDragSet.targets[i].layerRef.id)
			aDragSet.targets[i].enabled = false;
	}
}
	
function validateTarget(aDragSet, dropTargetName)
{
	for(var i = 0; i < aDragSet.targets.length; i++)
	{
		if(dropTargetName == aDragSet.targets[i].layerRef.id)
			aDragSet.targets[i].enabled = true;
	}
}

function compactArray(anArray)
{
	var tempArray = new Array();
	for(var i = 0; i < anArray.length; i++)
	{
		if(anArray[i] != null)
			tempArray[tempArray.length] = anArray[i];
	}
	return tempArray;
}


function checkDrop(dragLayerName)
{
	var theDragSet;
	var currentTarget = null;
	var tmp1,tmp2,pointX,pointY;
	var targetHit = false;
	var length,width;
	var tempWidth = new String();
	var tempHeight = new String();	
	theDragSet = getLayerRef(dragLayerName,document);
	
	lastItemDragged = theDragSet;
	
	//ASSERT:  dragLayerName is the name of a drag layer.
	if(debugMode == true)
	{
		if(theDragSet.myObjectType != ID_DRAGLAYER)
		{
			alert("Error in checkDrop:  dragLayerName (parameter 0) is not the name of a drag layer.");
			return null;
		}
	}
	//End debug.
	
	theDragSet = theDragSet.objectRef;  //Get a reference to the layer's drag set object.
	lastItemDragged = theDragSet;
	
	//If this object can no longer be dragged, then just return.
	if(theDragSet.enabled == false)
		return false;
		
	//Break current target references for the target that this object occupies.
	if (theDragSet.occupies != null)
	{		
		previousTarget = currentTarget = theDragSet.occupies;
		
		if(isCorrectTarget(theDragSet,currentTarget) == true)
			correctDroppedCount--;
			
		/*If multi drops are not allowed for this target, then just set occupiers[0] to null.*/
		if(currentTarget.multiDropEnabled == false)
			currentTarget.occupiers[0] = null;
		else
		{
			//Iterate through all occupiers and remove the one that points to theDragSet.
			for(var i = 0; i < currentTarget.occupiers.length; i++)
			{
					if(currentTarget.occupiers[i] == theDragSet)
						currentTarget.occupiers[i] = null;
			}	
			//Remove null object references from the target's occupiers array.
			//This step needs to be optimized!
			currentTarget.occupiers = new compactArray(currentTarget.occupiers);  
		}
		
		//Break current target reference for drag object.
		theDragSet.occupies = null;
		
		//There is now one less dropped item.
		droppedCount--;
	}
	// New by Scott
	else
	{
		previousTarget = null;
	}
		
	if(isNS)
	{
		if(useLayerBounds == false)
		{
			pointX = theDragSet.layerRef.left;
			pointY = theDragSet.layerRef.top;
		}
		else
		{
			pointX = (theDragSet.layerRef.left + theDragSet.layerRef.clip.width) / 2;
			pointY = (theDragSet.layerRef.top + theDragSet.layerRef.clip.height) / 2;
		}
	}
	else
	{
		if(useLayerBounds == false)
		{
			pointX = theDragSet.layerRef.style.pixelLeft;
			pointY = theDragSet.layerRef.style.pixelTop;
		}
		else
		{
			tempWidth = theDragSet.layerRef.style.width;
			tempHeight = theDragSet.layerRef.style.height;

			pointX = (parseInt(theDragSet.layerRef.style.pixelLeft) + (parseInt(tempWidth.replace(/px/i,""))) / 2);
			pointY = (parseInt(theDragSet.layerRef.style.pixelTop) + (parseInt(tempHeight.replace(/px/i,""))) / 2);
		}
	}
	
	//Check to see if any targets have been hit.
	for(var i = 0; i < theDragSet.targets.length; i++)
	{
		//Get coordinates of drop target.
		if(navigator.appName == "Netscape")
		{
			if(useLayerBounds == false)
			{
				tmp1 = Math.pow((theDragSet.targets[i].layerRef.left - pointX),2)
				tmp2 = Math.pow((theDragSet.targets[i].layerRef.top - pointY),2)
			}
			else
			{
				tmp1 = theDragSet.targets[i].layerRef.left;
				tmp2 = theDragSet.targets[i].layerRef.top;
				width = theDragSet.targets[i].layerRef.clip.width;
				height = theDragSet.targets[i].layerRef.clip.height;
			}
		}
		else
		{
			if(useLayerBounds == false)
			{
				tmp1 = Math.pow((theDragSet.targets[i].layerRef.style.pixelLeft - pointX),2)
				tmp2 = Math.pow((theDragSet.targets[i].layerRef.style.pixelTop - pointY),2) 	
			}
			else
			{
				tmp1 = theDragSet.targets[i].layerRef.style.pixelLeft;
				tmp2 = theDragSet.targets[i].layerRef.style.pixelTop;

				tempWidth = new String();
				tempWidth = theDragSet.targets[i].layerRef.style.width;
				width = parseInt(tempWidth.replace(/px/i,""));

				tempHeight = new String();
				tempHeight = theDragSet.targets[i].layerRef.style.height;
				height = parseInt(tempHeight.replace(/px/i,""));
			}
		}
		if(useLayerBounds == false)
		{
			targetHit = ((theDragSet.targets[i].enabled == true) && (Math.sqrt(tmp1 + tmp2)) < DRAG_DROP_DISTANCE);
		}
		else
		{
			targetHit = (theDragSet.targets[i].enabled == true) && ((pointX >= tmp1) && (pointX <= (tmp1 + width)) && ((pointY >= tmp2) && (pointY <= tmp2 + height)));
		}
			
		//Check to see if a target was hit.  If the target is disabled, then count it as a miss.
		if(targetHit == true)
		{	
			droppedCount++;  //One more item dropped.
			lastItemDragged = theDragSet;  //Set this to track last item dragged.
			
			currentTarget = theDragSet.targets[i];
			/*If a target was hit, check to see if the drag layer can be snapped onto it.
			  Snap can only occur if target.multiDropEnabled is false and 
			  target.occupiers[0] is null or target.multiDropEnabled is true.  In all other 
			  cases, the drag layer is snapped back to its original position.*/
			
			
			/*Check for wrong drop.  A wrong drop occurs at any time when evalMask[i] is false and
			  multidrop is true, or evalMask[i] is false, multidrop is false, and the target is 
			  unoccupied.*/
			  if(theDragSet.evalMask[i] == false)
			  {
			  		if((currentTarget.multiDropEnabled == false && currentTarget.occupiers[0] == null) || currentTarget.multiDropEnabled == true)
			  		{
			  			//Handle as incorrect.
			  			lastItemDraggedIncorrect = theDragSet;
						
						if((theDragSet.snapBack & SNAPBACK_INCORRECT) > 0)
						{
							droppedCount--;
							snapToOrigin(theDragSet);
						}
						else
						{
							snapToTarget(theDragSet,i);
				
							//Set target references
							theDragSet.occupies = theDragSet.targets[i];
				
							//If multiDropEnabled is not true, then set only the first array entry in the occupiers array
							if(currentTarget.multiDropEnabled == false)
								currentTarget.occupiers[0] = theDragSet
							else
							//Set the next available occupiers entry
							currentTarget.occupiers[currentTarget.occupiers.length] = theDragSet;
						}
					}
					else
					{
						snapToOrigin(theDragSet);
					}
					statusArray = new getStatusArray();
					eval(theDragSet.onIncorrectDrop)
					return true;
				}
				else
				/*Check for right drop.  A right drop occurs when evalMask[i] is true and multidrop is
				  true, or the target is unoccupied and multidrop is true.*/
				if(theDragSet.evalMask[i] == true)
				{
					if(currentTarget.multiDropEnabled == true || currentTarget.occupiers[0] == null)	
					{
						//Handle as correct.
			  			snapToTarget(theDragSet,i);
				
						//Set target references
						theDragSet.occupies = theDragSet.targets[i];
				
						//If multiDropEnabled is not true, then set only the first array entry in the occupiers array
						if(currentTarget.multiDropEnabled == false)
							currentTarget.occupiers[0] = theDragSet
						else
						//Set the next available occupiers entry
						currentTarget.occupiers[currentTarget.occupiers.length] = theDragSet;
						
						lastItemDraggedCorrect = theDragSet;
						correctDroppedCount++;
						
					
						if((theDragSet.snapBack & SNAPBACK_CORRECT) > 0)
							snapToOrigin(theDragSet);
							
						statusArray = new getStatusArray();
						eval(theDragSet.onCorrectDrop);
						return true;
						
					}
				}//End if
		}//End if			
	}//end for
	//Handle as miss	
	//droppedCount--;
			
	if((theDragSet.snapBack & SNAPBACK_MISS) > 0)
		snapToOrigin(theDragSet);
		
	statusArray = new getStatusArray();
	eval(theDragSet.onMiss);					
	return false;			
}//End function


//makeDragItem() creates a drag item using a layer reference.  Like makeDropTarget(), use the
//primitive or automated functions to set this object's properties.
function makeDragItem(layerRef)
{
	var theLayer = null;
	var limitTop = 0;
	var limitBottom = 0;
	var limitLeft = 0;
	var limitTop = 0;
	
	//Get the appropriate layer reference.	
	theLayer = getLayerRef(layerRef,document);
	
	//DEBUG: Assert theLayer exists and theLayer.id exists.
	if(debugMode == true)
	{
		if(theLayer == null || theLayer.id == null)
		{
			alert("Error in makeDragItem(): argument[0] is not a layer reference.");
			return null;
		}
	}
	//End debug mode
	
	//Set object properties.
	this.occupies = null;  //References dropTarget object that this object occupies.
	
	this.evalMask = new Array();  //Array of booleans
	this.targets = new Array();	//Array of references to drop targets
	
	this.onCorrectDrop = "";      //Can be set to evaluate JS on a correct drop.
	this.onIncorrectDrop = "";		//Can be set to evaluate JS on an incorrect drop.
	this.onMiss = "";			//Can be set to evaluate JS on a miss.
	
	this.snapBack = 0;	//Controls when object should be placed in its original position.
	this.enabled = true;	//Makes object a valid draggable object.
	this.layerRef = theLayer;	//References actual draggable layer.
	this.myObjectType = ID_DRAGITEM;  //Tags object with appropriate identifier.
	
	if(isNS)
	{
		this.originTop = theLayer.top;
		this.originLeft = theLayer.left;
		this.width = theLayer.clip.width;
		this.height = theLayer.clip.height;
	}
	else
	{
		this.originTop = theLayer.style.pixelTop;
		this.originLeft = theLayer.style.pixelLeft;
		this.width = theLayer.offsetWidth;
		this.height = theLayer.offsetHeight;
	}
	
	//Calculate constraints to keep drag layer on screen.
	limitLeft = this.originLeft;
	limitTop = this.originTop;
	limitRight = screenExtentX - (this.originLeft + this.width);
	limitBottom = screenExtentY - (this.originTop + this.height);
	
	//The following line of code should be replaced with custom dragdrop code at a later date.
	MM_dragLayer('document.layers["' + theLayer.id + '"]','document.all["' + theLayer.id + '"]',0,0,0,0,true,false,limitTop,limitBottom,limitLeft,limitRight,false,false,0,'checkDrop("' + theLayer.id + '")',true,'')

	theLayer.objectRef = this;	//Acutal draggable layer points to this object.
	theLayer.myObjectType = ID_DRAGLAYER;  //Tags drag layer with appropriate identifier.
	
	dragItemArray[dragItemArray.length] = this;
	return this;
}
	
/*addTargetsToDragItem() adds one or more targets to the aDragItem.  The targets must be placed
  into an array.  The targets can be dropTarget objects or layer references.*/
function addTargetsToDragItem(aDragItem,targetArray)
{
	var i = 0;
	
	//DEBUG:  Assert aDragItem is a layer reference or dragItem object
	if(debugMode == true)
	{
		if((aDragItem.myObjectType != ID_DRAGITEM) && (aDragItem.id == null))
		{
			alert("Error in addTargetsToDragItem: Argument 1 is not a layer reference or dragItem object reference.")
			return false;
		}	
	}
	//End debug.
	
	//DEBUG:  Assert all items in targetArray are layer references or dropTarget objects
	if(debugMode == true)
	{
		for(var i = 0; i < targetArray.length; i++)
		{
			if(targetArray[i].myObjectType != ID_DROPTARGET)
			{
				alert("Error in addTargetsToDragItem:  targetArray[" + i + "] is not a layer reference or dropTarget object reference.")
				return false;
			}
		}
	}
	//End debug.
	
	aDragItem.targets = new Array(targetArray.length);
	aDragItem.evalMask = new Array(targetArray.length);
	
	for(var i = 0; i < targetArray.length; i++)
	{
		aDragItem.targets[i] = targetArray[i];  //Copy reference into targets array.	
		aDragItem.evalMask[i] = true;  	//Initialize all drop targets as valid.
	}
	return true;
}

/*makeDragSet() creates an association between a dragItem layer and one or more dropTarget layers.
  Whenever an item is dragged and/or dropped, its drag set association is evaluated and action
  is taken depending on its settings as well as the settings for the drag item and drop target.
  NOTE: This function expects the first argument to be a draggable layer or dragLayer object,
  and the remaining arguments to all be drop target layers or dropTarget objects.  If a dragLayer
  already references a dropTarget prior to a call to makeDragSet(), the new reference is ignored.*/
function makeDragSet(theDragObject,theDropTargetArray)
{
	var tempArray = new Array();
	var assertVar;

	//DEBUG: Assert that theDragLayer is a dragObject
	if(debugMode == true)
	{
		if(theDragObject.myObjectType != ID_DRAGITEM)
		{
			alert("Error in makeDragSet(): argument[0] is not a Drag Layer object reference.");
			return null;
		}

	}
	//End debug
	
	assertVar = addTargetsToDragItem(theDragObject,theDropTargetArray);
		
	//ASSERT:  All target references were able to be assigned.
	if(debugMode == true && assertVar != true)
	{
		alert("Error:  Call to addTargetsToDragItems() failed (returned " + assertVar + ".");
		return null;
	}
	
	return theDragObject;
}				


function breakReferences(theDragSet)
{
	var currentTarget = null;
	
	//Break current target references for the target that this object occupies.
	if(theDragSet.occupies != null)
	{		
		currentTarget = theDragSet.occupies;
		
		if(isCorrectTarget(theDragSet,currentTarget) == true)
			correctDroppedCount--;
			
		/*If multi drops are not allowed for this target, then just set occupiers[0] to null.*/
		if(currentTarget.multiDropEnabled == false)
			currentTarget.occupiers[0] = null;
		else
		{
			//Iterate through all occupiers and remove the one that points to theDragSet.
			for(var i = 0; i < currentTarget.occupiers.length; i++)
			{
					if(currentTarget.occupiers[i] == theDragSet)
						currentTarget.occupiers[i] = null;
			}	
			//Remove null object references from the target's occupiers array.
			//This step needs to be optimized!
			currentTarget.occupiers = new compactArray(currentTarget.occupiers);  
		}
		
		//Break current target reference for drag object.
		theDragSet.occupies = null;
		
		//There is now one less dropped item.
		droppedCount--;
	}
}	

function resetDragItem(theDragItem)
{
	breakReferences(theDragItem);
	snapToOrigin(theDragItem);
}

function setSnapBack(dragItem,snapBackValue)
{
	dragItem.snapBack = snapBackValue;
}

function disableDrag(dragItem)
{
	dragItem.layerRef.MM_dragOk = null;
}

function enableDrag(dragItem)
{
	dragItem.layerRef.MM_dragOk = true;
}

/*AutoInitDragItems() will automatically make any layer on the page with an id of
  "DragItem" + a number into a dragItem object.  The items must be 
  numbered starting with 1, and no numbers may be skipped.*/
function autoInitDragItems(correctJS, incorrectJS, missJS,snapBack)
{		
	var layerRef = null;
	var counter = 1;
	while ((layerRef = getLayerRef("DragLayer" + counter)) != null)
	{
			var testObj = new makeDragItem(layerRef.id);
			setOnCorrectDrop(dragItemArray[counter-1], correctJS);
			setOnIncorrectDrop(dragItemArray[counter-1], incorrectJS);
			setOnMiss(dragItemArray[counter-1], missJS);	
			setSnapBack(dragItemArray[counter-1],snapBack);
			counter++;
	}
}

function autoCreateDragSets()
{
	var endNumber = 0;
	var correctDropTarget = null;
	//For each draggable item, allow drops into all targets.
	for(var i = 0; i < dragItemArray.length; i++)	
	{
		endNumber = dragItemArray[i].layerRef.id.substring("DragLayer".length);
		
		makeDragSet(dragItemArray[i],dropTargetArray);
		
		//Mask off all targets.
		for(var j = 0; j < dragItemArray[i].evalMask.length; j++)
		{
			/*Find a target with a matching end number (i.e. 'DragItem2' corresponds to 'DropTarget2')
			  Set that target's corresponding evalMask cell to true.*/
			if(dragItemArray[i].targets[j].layerRef.id == "DropTarget" + endNumber)
				dragItemArray[i].evalMask[j] = true;
			else
				dragItemArray[i].evalMask[j] = false;
		}
	}
}
			
	
function autoInitDropTargets()
{
	var layerRef = null;
	var counter = 1;
	while ((layerRef = getLayerRef("DropTarget" + counter)) != null)
	{
		testObject = new makeDropTarget(layerRef.id);
		counter++;
	}
}
	

//MM_dragLayer is Macromedia Dreamweaver drag and drop code.  This code will be replaced with
//custom drag and drop code in the future.
function MM_dragLayer(objNS,objIE,hL,hT,hW,hH,toFront,dropBack,cU,cD,cL,cR,targL,targT,tol,dropJS,et,dragJS) { //v2.0
  //Copyright 1998 Macromedia, Inc. All rights reserved.
  var i,j,aLayer,retVal,curDrag=null,NS=(navigator.appName=='Netscape'), curLeft, curTop;
  if (!document.all && !document.layers) return false;
  retVal = true; if(!NS && event) event.returnValue = true;
  if (MM_dragLayer.arguments.length > 1) {
    curDrag = eval((NS)?objNS:objIE); if (!curDrag) return false;
    if (!document.allLayers) {
      document.allLayers = new Array();
      with (document) {
        if (NS) {
          for (i=0; i<layers.length; i++) allLayers[i]=layers[i];
          for (i=0; i<allLayers.length; i++) {
            if (allLayers[i].document && allLayers[i].document.layers)
              for (j=0; j<allLayers[i].document.layers.length; j++)
                allLayers[allLayers.length] = allLayers[i].document.layers[j];
        } } else {
          for (i=0; i<all.length; i++)
            if (all[i].style != null && all[i].style.position)
              allLayers[allLayers.length] = all[i];
    } } }
    curDrag.MM_dragOk=true; curDrag.MM_targL=targL; curDrag.MM_targT=targT;
    curDrag.MM_tol=Math.pow(tol,2); curDrag.MM_hLeft=hL; curDrag.MM_hTop=hT;
    curDrag.MM_hWidth=hW; curDrag.MM_hHeight=hH; curDrag.MM_toFront=toFront;
    curDrag.MM_dropBack=dropBack; curDrag.MM_dropJS=dropJS;
    curDrag.MM_everyTime=et; curDrag.MM_dragJS=dragJS;
    curDrag.MM_oldZ = (NS)?curDrag.zIndex:curDrag.style.zIndex;
    curLeft= (NS)?curDrag.left:curDrag.style.pixelLeft; curDrag.MM_startL = curLeft;
    curTop = (NS)?curDrag.top:curDrag.style.pixelTop; curDrag.MM_startT = curTop;
    curDrag.MM_bL=(cL<0)?null:curLeft-cL; curDrag.MM_bT=(cU<0)?null:curTop -cU;
    curDrag.MM_bR=(cR<0)?null:curLeft+cR; curDrag.MM_bB=(cD<0)?null:curTop +cD;
    curDrag.MM_LEFTRIGHT=0; curDrag.MM_UPDOWN=0; curDrag.MM_SNAPPED=false; //use in your JS!
    document.onmousedown = MM_dragLayer; document.onmouseup = MM_dragLayer;
    if (NS) document.captureEvents(Event.MOUSEDOWN|Event.MOUSEUP);
  } else {
    var theEvent = ((NS)?objNS.type:event.type);
    if (theEvent == 'mousedown') {
      var aLayer, maxDragZ=null;
      var mouseX = (NS)?objNS.pageX : event.clientX + document.body.scrollLeft;
      var mouseY = (NS)?objNS.pageY : event.clientY + document.body.scrollTop;
      document.MM_maxZ = 0;
      for (i=0; i<document.allLayers.length; i++) {
        aLayer = document.allLayers[i];
        var aLayerZ = (NS)?aLayer.zIndex:aLayer.style.zIndex;
        if (aLayerZ > document.MM_maxZ) document.MM_maxZ = aLayerZ;
        var isVisible = (((NS)?aLayer.visibility:aLayer.style.visibility).indexOf('hid') == -1);
        if (aLayer.MM_dragOk != null && isVisible) with (aLayer) {
          var parentL=0; var parentT=0;
          if (!NS) { 
            parentLayer = aLayer.parentElement;
            while (parentLayer != null && parentLayer.style.position) {
              parentL += parentLayer.offsetLeft;
              parentT += parentLayer.offsetTop;
              parentLayer = parentLayer.parentElement;
          } }
          var tmpX=mouseX-(((NS)?pageX:style.pixelLeft+parentL)+MM_hLeft);
          var tmpY=mouseY-(((NS)?pageY:style.pixelTop +parentT)+MM_hTop);
          var tmpW = MM_hWidth;  if (tmpW <= 0) tmpW += ((NS)?clip.width :offsetWidth);
          var tmpH = MM_hHeight; if (tmpH <= 0) tmpH += ((NS)?clip.height:offsetHeight);
          if ((0 <= tmpX && tmpX < tmpW && 0 <= tmpY && tmpY < tmpH) &&
             (maxDragZ == null || maxDragZ <= aLayerZ)) {
            curDrag = aLayer; maxDragZ = aLayerZ;
      } } }
      if (curDrag) {
        document.onmousemove = MM_dragLayer; 
        if (NS) document.captureEvents(Event.MOUSEMOVE);
        curLeft = (NS)?curDrag.left:curDrag.style.pixelLeft;
        curTop = (NS)?curDrag.top:curDrag.style.pixelTop;
        MM_oldX = mouseX - curLeft; MM_oldY = mouseY - curTop;
        document.MM_curDrag = curDrag;  curDrag.MM_SNAPPED=false;
        if(curDrag.MM_toFront) {
          eval('curDrag.'+((NS)?'':'style.')+'zIndex=document.MM_maxZ+1');
          if (!curDrag.MM_dropBack) document.MM_maxZ++;
        }
        retVal = false; if(!NS) event.returnValue = false;
    } } else if (theEvent == 'mousemove') {
      if (document.MM_curDrag) with (document.MM_curDrag) {
        var mouseX = (NS)?objNS.pageX : event.clientX + document.body.scrollLeft;
        var mouseY = (NS)?objNS.pageY : event.clientY + document.body.scrollTop;
        newLeft = mouseX-MM_oldX; newTop  = mouseY-MM_oldY;
        if (MM_bL!=null) newLeft = Math.max(newLeft,MM_bL);
        if (MM_bR!=null) newLeft = Math.min(newLeft,MM_bR);
        if (MM_bT!=null) newTop  = Math.max(newTop ,MM_bT);
        if (MM_bB!=null) newTop  = Math.min(newTop ,MM_bB);
        MM_LEFTRIGHT = newLeft-MM_startL; MM_UPDOWN = newTop-MM_startT;
        if (NS) {left = newLeft; top = newTop;}
        else {style.pixelLeft = newLeft; style.pixelTop = newTop;}
        if (MM_dragJS) eval(MM_dragJS);
        retVal = false; if(!NS) event.returnValue = false;
    } } else if (theEvent == 'mouseup') {
      document.onmousemove = null;
      if (NS) document.releaseEvents(Event.MOUSEMOVE);
      if (NS) document.captureEvents(Event.MOUSEDOWN); //for mac NS
      if (document.MM_curDrag) with (document.MM_curDrag) {
        if (typeof MM_targL =='number' && typeof MM_targT == 'number' &&
            (Math.pow(MM_targL-((NS)?left:style.pixelLeft),2)+
             Math.pow(MM_targT-((NS)?top:style.pixelTop),2))<=MM_tol) {
          if (NS) {left = MM_targL; top = MM_targT;}
          else {style.pixelLeft = MM_targL; style.pixelTop = MM_targT;}
          MM_SNAPPED = true; MM_LEFTRIGHT = MM_startL-MM_targL;
          MM_UPDOWN = MM_startT-MM_targT;
        }
        if (MM_everyTime || MM_SNAPPED) eval(MM_dropJS);
        if(MM_dropBack) {if (NS) zIndex = MM_oldZ; else style.zIndex = MM_oldZ;}
        retVal = false; if(!NS) event.returnValue = false;
      } 
      document.MM_curDrag = null;
    }
    if (NS) document.routeEvent(objNS);
  }
  return retVal;
}

function setTargetAsCorrect(aDragSet,dropTargetName)
{
	for(var i = 0; i < aDragSet.targets.length; i++)
	{
		if(dropTargetName == aDragSet.targets[i].layerRef.id)
			aDragSet.evalMask[i] = true;
	}
}

function setTargetAsIncorrect(aDragSet,dropTargetName)
{
	for(var i = 0; i < aDragSet.targets.length; i++)
	{
		if(dropTargetName == aDragSet.targets[i].layerRef.id)
			aDragSet.evalMask[i] = false;
	}
}


function inspectDragSet(aDragSet, dragSetIndex)
{
	var reportString;
	
	if(dragSetIndex == null)
		reportString = "Unindexed Drag Set: ";
	else
		reportString = "Drag Set # " + dragSetIndex + ":";
		
	reportString += "\nDrag Item layer is " + aDragSet.layerRef.id;
	reportString += "\nDrop targets are:";
	
	for(var i = 0; i < aDragSet.targets.length; i++)
	{
		reportString += "\n" + aDragSet.targets[i].layerRef.id;
		if(aDragSet.evalMask[i] == true)
			reportString += " (Correct)";
	}
			
	alert(reportString)
}

function getDragItemNumber(layerRef)
{
	var num = -1;
	for (var i=0;i<dragItemArray.length;i++)
	{
		if (dragItemArray[i] == layerRef)
		{
			num = (i+1);
		}
	}
	return num;
}
